package dp.ua.epic.resource;

import dp.ua.epic.webapp.EpicApplication;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * Scan for aviables locales.Aviable locale is the one 
 * locale that has all the files its localization
 * 
 * @author Chris
 * @date 07.02.2011 16:29:14
 */
public class LocaleDetector {

    private static final String PROPERTY_FILE_EXTENSION = ".properties";
    private static final String XML_FILE_EXTENSION = ".xml";
    private static final String COUNTRY_REGION_SEPARATOR = "_";
    private static final String FILE_EXTENSION_SEPARATOR = ".";

    /**
     * Scan directories that contains in file directories.properties
     * for file with extension '.properties' or '.xml'.Then, finding all page
     * (files that have similar prefix (page name) but have different extension (or doesnt have it at all if it is base locale).
     * ...
     * @return List of aviable(support for all pages) locales
     */
    public static List<Locale> getAviableLocales(String fileLocation) {
        List<Locale> aviableLocales = new ArrayList<Locale>();
        List<String> allPropertyFiles = loadPropertyFiles(fileLocation);

        if (allPropertyFiles.isEmpty()) {
            Logger.getLogger(EpicApplication.class.getName()).log(Level.SEVERE, "There are no localization property files!");
            return aviableLocales;
        }

        Map<String, List<String>> groupedPropertyFiles = groupPropertyFileByPrefix(allPropertyFiles);
        List<String> aviableLocalesSuffixes = extractAviableLocalesSignatures(groupedPropertyFiles);
        boolean arrayThatContainLocalizationPresents[] = checkAllOccurrencesOfLocalizations(groupedPropertyFiles, aviableLocalesSuffixes);

        //If we didnt count some file with base localization
        if (arrayThatContainLocalizationPresents[0] == false) {
            Logger.getLogger(EpicApplication.class.getName()).log(Level.SEVERE, "There are no some file(s) with base localization");
            return aviableLocales;
        }

        //Add only fully existing localization to our localizations
        for (int i = 1; i < arrayThatContainLocalizationPresents.length; i++) {
            if (arrayThatContainLocalizationPresents[i]) {
                String localizationSuffix = aviableLocalesSuffixes.get(i - 1);
                aviableLocales.add(new Locale(localizationSuffix.substring(1, 3), localizationSuffix.substring(4, 6)));
            }
        }

        return aviableLocales;
    }

    /**
     * Load all names of property files that was found in 
     * directories with are described in configFileLocation
     * 
     * @param configFileLocation
     * @return 
     */
    private static List<String> loadPropertyFiles(String configFileLocation) {
        List<String> allPropertyFiles = new LinkedList<String>();

        try {
            PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
            BufferedReader br = new BufferedReader(new InputStreamReader(LocaleDetector.class.getResourceAsStream(configFileLocation)));
            String line = br.readLine();

            //add files in directories to allPropertyFiles
            while (line != null) {
                for (Resource res : patternResolver.getResources(line)) {
                    final String fileName = res.getFilename();
                    if (fileName.endsWith(PROPERTY_FILE_EXTENSION) || fileName.endsWith(XML_FILE_EXTENSION)) {
                        allPropertyFiles.add(fileName);
                    }
                }
                line = br.readLine();

            }
            br.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(EpicApplication.class.getName()).log(Level.SEVERE, "File not found " + ex.getMessage());
        } catch (IOException ex) {
            Logger.getLogger(EpicApplication.class.getName()).log(Level.SEVERE, "Got IO Exception " + ex.getMessage());
        } finally {
            return allPropertyFiles;
        }
    }

    /**
     * Group property file names by it prefix. Store this data in map where
     * suffix is key and names stored in value.
     * 
     * @param allFiles
     * @return 
     */
    private static Map<String, List<String>> groupPropertyFileByPrefix(List<String> allFiles) {
        Map<String, List<String>> groupedFiles = new HashMap<String, List<String>>();

        for (String file : allFiles) {
            String prefix = (file.indexOf(COUNTRY_REGION_SEPARATOR) > -1) ? (file.substring(0, file.indexOf(COUNTRY_REGION_SEPARATOR))) : (file.substring(0, file.indexOf(FILE_EXTENSION_SEPARATOR)));

            if (!groupedFiles.containsKey(prefix)) {
                groupedFiles.put(prefix, new LinkedList<String>());
            }

            groupedFiles.get(prefix).add(file.substring(0, file.indexOf(FILE_EXTENSION_SEPARATOR)));
        }

        return groupedFiles;
    }

    /**
     * Extract all aviable localization suffixes from grouped files.
     * @param groupedFiles
     * @return 
     */
    private static List<String> extractAviableLocalesSignatures(Map<String, List<String>> groupedFiles) {
        int totalLocalesCount = 0;
        String biggestLocalesCountKey = "";

        for (String key : groupedFiles.keySet()) {
            if (totalLocalesCount < groupedFiles.get(key).size()) {
                totalLocalesCount = groupedFiles.get(key).size();
                biggestLocalesCountKey = key;
            }
        }

        List<String> aviableLocalesSuffixes = new ArrayList<String>();

        for (String file : groupedFiles.get(biggestLocalesCountKey)) {
            if (file.indexOf(COUNTRY_REGION_SEPARATOR) > -1) {
                aviableLocalesSuffixes.add(file.substring(file.indexOf(COUNTRY_REGION_SEPARATOR), file.length()));
            }
        }

        return aviableLocalesSuffixes;
    }

    /**
     * Check for occurences of localizations in all grouped files. 
     * Where [0] is occurence of base localization in all grouped files
     * 
     * @param groupedFiles
     * @param aviableSuffixes
     * @return 
     */
    private static boolean[] checkAllOccurrencesOfLocalizations(Map<String, List<String>> groupedFiles, List<String> aviableSuffixes) {
        boolean occurrences[] = new boolean[aviableSuffixes.size() + 1];

        for (int i = 0; i < occurrences.length; i++) {
            occurrences[i] = true;
        }

        for (String key : groupedFiles.keySet()) {
            List<String> locales = groupedFiles.get(key);

            // If size of list with different variations of locale
            // (include base) less than 2, we think that is not file with locale
            if (locales.size() < 2) {
                continue;
            }

            int index = 0;
            if (!locales.contains(key)) {
                Logger.getLogger(EpicApplication.class.getName()).log(Level.WARNING, "Could not find base locale for '" + key + "'");
                occurrences[index++] = false;
            }
            for (String pair : aviableSuffixes) {
                if (!locales.contains(key + pair)) {
                    Logger.getLogger(EpicApplication.class.getName()).log(Level.WARNING, "Could not find locale '" + pair + "' for '" + key + "' + [" + key + pair + "]");
                    occurrences[index++] = false;
                }
            }
        }

        return occurrences;
    }
}
